/**
 * 
 */
package controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

import javax.swing.SwingUtilities;
import javax.swing.Timer;

import model.AttackStageState;
import model.GameDriver;
import model.PauseMenuState;
import model.SetupStageState;
import view.CargoItemPanel;
import view.Frame;
import view.MapPanel;
import view.StoreItemPanel;

import common.GridCoordinate;

/**
 * @author Bill
 * 
 * The controller class for communicating between the view and
 * the model. When view events occur, they call a method of this class,
 * which in turn calls methods of the model
 *
 */
public class Controller {
	
	//private static Frame frame = new Frame();
	private static Frame frame = (Frame) SwingUtilities.getWindowAncestor(Frame.getCards()); //This is wrong
	private static GameDriver driver = frame.getGameDriver();
	
	//used to keep track of ticks 
	static private int tickCount = 0;
	static public int ticksPerSecond = 50;	
	static private boolean paused = false;
	static private boolean disableNewMessages = false;
	
	static ActionListener tickListener = new ActionListener(){
		@Override
		public void actionPerformed(ActionEvent arg0) {
			frame.onTick(tickCount);
			++tickCount;
		}
	};
	static private Timer tickTimer = new Timer(20,tickListener);
	static private ArrayList<TickObserver> tickObservers = new ArrayList<TickObserver>(); 
	
	/**
	 * Starts the timer running
	 */
	static public void startTimer(){
		tickTimer.start();
	}
	
	/**
	 * Stops the timer
	 */
	static public void stopTimer(){
		tickTimer.stop();
		tickCount = 0;
	}
	
	/**
	 * Pause the timer but do not reset tickCount
	 */
	static public void pauseTimer(){
		if(driver.getCurrentState() instanceof AttackStageState || driver.getCurrentState() instanceof PauseMenuState){
			if(paused == false){
				tickTimer.stop();
				paused = true;
			} else {
				tickTimer.start();
				paused = false;
			}
		}
	}
	
	/**
	 * Called when a view input event requires the model to change
	 * to the next state
	 */
	static public void advanceState(){
		removeMessage();
		driver.advanceState();
	}
	
	/**
	 * Called when a view input event requires the model to change
	 * to the next state
	 */
	static public void returnToMainMenu(){
		driver.returnToMainMenu();
	}
	
	/**
	 * Performs an action depending on which tile was clicked. Can
	 * affect both the model and the view
	 * 
	 * @param tile
	 */
	static public void tileClicked(GridCoordinate tile){
		removeMessage();
		driver.tileClicked(tile);
	}
	
	/**
	 * Called when the mouse enters a tile on the map grid. It then
	 * decides what action to take based on the current state
	 * 
	 * @param tile
	 */
	static public boolean hoverOverTile(GridCoordinate tile){
		if(driver.getCurrentState() instanceof AttackStageState){
			frame.getGamePanel().getMapPanel().setSelectedTile(tile);
			driver.getMap().setTargetX((float)tile.getX()+0.5f);
			driver.getMap().setTargetY((float)tile.getY()+0.5f);
		}
		else if(driver.getCurrentState() instanceof SetupStageState){
			
		}
		return driver.getStore().isItemSelected();
	}
	
	/**
	 * Called when a store item is clicked. Modifies the 
	 * model's store class
	 * 
	 * @param itemName
	 * @return true if the model returns true
	 */
	static public boolean storeItemSelected(StoreItemPanel item){
		if(driver.getStore().selectItem(item.getItemName())){
			//frame.getGamePanel().selectInventoryItem(item);
			return true;
		}
		return false;
	}
	
	/**
	 * Called when a cargo item is clicked. Modifies the 
	 * model's cargo class
	 * 
	 * @param weaponName
	 * @return true if the model returns true
	 */
	static public boolean cargoItemSelected(CargoItemPanel item){
		if(driver.getPlayer().getCargo().selectWeapon(item.getItemName())){
		//item.highlightItem();
		return true;
		}
		return false;
	}
	
	/**
	 * Called when the background of the inventory is selected.
	 * Causes the model's store to "deselect" the currently selected
	 * item, if there is one.
	 */
	static public void inventoryBackgroundClicked(){
		driver.getStore().deselectItem();
		driver.getPlayer().getCargo().deselectWeapon();
	}	
	
	/**
	 * Updates the view whenever needed
	 */
	static public void updateView(){
		frame.updateGamePanel();
	}
	
	/**
	 * Displays a message on the map panel for a specified amount of milliseconds
	 */
	static public void displayMessage(String message, int duration){
		//System.out.println("HERE at display message"+disableNewMessages);
		removeMessage();
		if(!disableNewMessages){
			Timer messageTimer = new Timer(duration, new ActionListener(){
				@Override
				public void actionPerformed(ActionEvent arg0) {
					removeMessage();
				}
			});
			frame.getGamePanel().getMapPanel().displayMessage(message);
			messageTimer.setRepeats(false);
			messageTimer.start();
		}
	}
	
	/**
	 * Displays a message on the map panel and disables other messages from overwriting this one for 
	 * the time in milliseconds specified by "int duration". After that duration is done, the message will
	 * be removed
	 * 
	 */
	static public void displayHighPriorityMessage(String message, int duration){
		Timer messageTimer = new Timer(duration, new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				disableNewMessages = false;
				removeMessage();
			}
		});
		frame.getGamePanel().getMapPanel().displayMessage(message);
		disableNewMessages = true;
		messageTimer.setRepeats(false);
		messageTimer.start();
	}
	
	/**
	 * Displays a message on the map panel
	 */
	static public void displayMessage(String message){
		if(!disableNewMessages){
			frame.getGamePanel().getMapPanel().displayMessage(message);
		}
	}
	
	/**
	 * Removes a message on the map panel
	 */
	static public void removeMessage(){
		if(!disableNewMessages){
			frame.getGamePanel().getMapPanel().removeMessage();
		}
	}
	
	/**
	 * Reduces the player's health to zero
	 */
	static public void killPlayer(){
		driver.getPlayer().damagePlayer(driver.getPlayer().getMaxHealth());
	}
	
	/**
	 * Pauses the game
	 */
	static public void pauseGame(){
		driver.pauseGame();
		pauseTimer();
	}
	
	/**
	 * Draws a new map
	 */
	static public void createNewMapView(GameDriver gameDriver){
		//System.out.println( "Creating new Map panel for level "+gameDriver.getCurrentLevel());
		frame.getGamePanel().setMapPanel(new MapPanel(gameDriver.getMap().getTrailCoordinates(), gameDriver.getCurrentLevel()));
	}
	
	/**
	 * Adds a tick observer to the list of observers
	 * @param observer
	 */
	public static void addTickUpdateObserver(TickObserver observer){
		if(tickObservers == null) tickObservers = new ArrayList<TickObserver>(); 
		tickObservers.add(observer);
	}
	
	/**
	 * removes the observer from the list of observers
	 * @param observer
	 */
	public static void removeTickObserver(TickObserver observer){
		if(tickObservers == null) tickObservers = new ArrayList<TickObserver>(); 
		tickObservers.remove(observer);
	}
}
